home *** CD-ROM | disk | FTP | other *** search
- -----------------------------------------------------------------------------
- FIXDS Version 2.0 by Michael Geary 4/18/89
- -----------------------------------------------------------------------------
- Changes from version 1.0:
- Now processes the .EXE file instead of .OBJ for better reliability.
- -----------------------------------------------------------------------------
- Summary:
- Eliminates need for EXPORTS and MakeProcInstance() in Windows applications.
- Prevents bugs caused by omitting EXPORTS or MakeProcInstance().
- Allows Windows applications to export functions that will be called
- directly from a dynamic link library.
- -----------------------------------------------------------------------------
-
- Have you ever forgotten to EXPORT a window function? How about good
- old MakeProcInstance() - ever had a weirdo crash because you forgot
- this one? Worse yet, how about the time you tried to directly call a
- function you had EXPORTed? If you're like me, these have probably
- happened more times than you'd like to admit.
-
- With FIXDS, you will never have to use EXPORTS or MakeProcInstance()
- in a Windows application again! All you have to do is run FIXDS on
- your .EXE file. For example, in your MAKE file:
-
- foo.exe: foo.obj ...
- link @foo.lnk
- fixds foo.exe
- ...
-
- FIXDS can be run before or after RC; it doesn't matter.
-
- Now you can get rid of the EXPORTS and MakeProcInstance() nonsense
- you've been dealing with. You don't have to EXPORT window functions
- or any other callback functions. You don't have to do a
- MakeProcInstance() on any function, ever. And, it's perfectly safe
- to directly call any callback functions you may have.
-
- You can take out the entire EXPORTS section from your application's
- .DEF file. You can take out every MakeProcInstance() and
- FreeProcInstance() call. The places you used the return value from
- MakeProcInstance() (such as a DialogBox() call), you can just use the
- actual function name instead.
-
- Your code will even work if you leave in some MakeProcInstance's or
- EXPORTs. Once you start using FIXDS, they just become irrelevant -
- your code will work with or without them.
-
- FIXDS also gives you one useful capability that isn't ordinarily
- available. Your Windows application can export functions which will
- be directly called from a dynamic link library. This can be
- extremely handy. Some Windows applications can be extended by loading
- and calling DLL's at runtime. (SQLWindows and Excel are examples of
- this.) Normally, these DLL's can not make any calls to functions in
- the application. That's a bummer, since you probably have a slew of
- utility functions in the application that a custom DLL might put to
- good use. With normal Windows code, you can't just make normal C
- calls to these functions. You *could* do MakeProcInstance() on all
- of them and make a table of the addresses, and then pass this table
- to the DLL so it could make indirect calls to the application
- functions, but with FIXDS there is an easier way. Just list in the
- EXPORTS section of your application's .DEF file the functions you
- wish to make available to the DLL's. (This is the only case where
- you need an EXPORTS section, and only the functions you want
- available to DLL's should be listed.) Then run IMPLIB on this .DEF
- file and presto - you have a .LIB that you can link with your DLL's.
- The DLL's can then make direct calls to these functions, and the
- references will be fixed up at runtime just as if the called
- functions were in some other DLL.
-
-
- Now, you've probably got three questions:
-
- "What's the catch?"
-
- "How does this thing work, anyway?"
-
- "If it does work, why in the world has Microsoft been putting us
- through all the EXPORTS and MakeProcInstance() grief all this time?"
-
- Well, I can answer the first two questions. Darned if I know the
- answer to the third, though!
-
- First, what's the catch? Not much. FIXDS will work with any
- Windows application that has its own stack. (I read once that in
- theory it's possible to build an application that doesn't have its
- own stack, but I don't know if anyone has ever tried that. If your
- application doesn't have its own stack, FIXDS won't work with it.)
- FIXDS is for applications only, not dynamic link libraries. It
- depends on the fact that SS == DS, which is true only in application
- code.
-
- There's no other catch I can think of. FIXDS is completely compatible
- with every version of Windows, and with every compiler that generates
- the standard Windows function prolog.
-
- Now for the second question, how does this pup work? I'll give a
- brief explanation here; for more information on the same topic, see
- Chapter 8 of Charles Petzold's _Programming Windows_.
-
- The basic problem that EXPORTS and MakeProcInstance() try to solve is
- getting the proper value into the DS register. Each FAR function in
- a Windows application begins with this prolog:
-
- push ds ; May have "mov ax,ds" instead of these
- pop ax ; two instructions. Does the same thing.
- nop
- inc bp
- push bp
- mov bp, sp
- push ds
- mov ds, ax
-
- Now, aside for fooling around with BP, the net effect of this code is
- to put the same value into DS that it already had (and to save a copy
- of DS on the stack, which gets popped back off later). For a normal
- FAR function that you call from your own code, that's a lot of
- busywork, but doesn't hurt anything. After all, you already had the
- right DS value since it was your application that called this
- function.
-
- The busywork starts to come into play when you deal with functions
- that are called from outside your application, such as window
- functions and other callback functions. When Windows calls these
- functions, there is a different value in DS, so something has to
- change. That's what the EXPORTS and MakeProcInstance() do for you.
- Let's take the case of a dialog function. The dialog manager inside
- Windows (in USER.EXE) simply makes a direct FAR call to the address
- you give as your dialog function, and DS at that time points to
- USER's data segment, so how does DS get the right value for your
- application?
-
- Well, when you EXPORT your dialog function, Windows NOP's out the
- first two bytes of the prolog, so it looks like this:
-
- nop
- nop
- nop
- inc bp
- push bp
- mov bp, sp
- push ds
- mov ds, ax
-
- Now you can see that this prolog will end up moving the value from
- the AX register into DS (after saving the old DS value). That's why
- you can't just call this function directly from C code - it would
- take whatever happened to be laying around in AX and put it into DS;
- not a good idea. However, when you call MakeProcInstance(), Windows
- creates an "instance thunk", which looks like this:
-
- mov ax, XXXX
- jmp <TheFunction>
-
- That XXXX is the key - Windows keeps track of all instance thunks,
- and whenever your data segment moves in memory, it patches the XXXX
- to reflect the new address of your data segment. So, that's why you
- have to call MakeProcInstance() and then pass that address - the
- address of the instance thunk - into CreateDialog() or DialogBox().
- The dialog manager will then call your instance thunk, which puts the
- right value into AX, which the modified function prolog will then
- copy over to DS.
-
- (I'm leaving out all the details of "reload thunks" - they aren't
- relevant to this discussion.)
-
- For a window function, essentially the same thing goes on, except
- that you only have to do the EXPORTS and not the MakeProcInstance().
- That's because window functions are only called via SendMessage() or
- DispatchMessage(), and those functions essentially do the equivalent
- of the instance thunk internally - they put the proper value into AX
- before calling your window function.
-
- Well, that's all a lot of work just to get the right value into the
- DS register, but after all, Windows has to deal with the fact that
- your data segment may move around. Worse yet, it has to handle the
- fact that there may be multiple instances of your application. If it
- weren't for that, Windows could just patch your function prolog to
- put in the right DS value directly, like it does for dynamic link
- libraries. But, the function code is shared among all instances, and
- they all have different data segment addresses. That's why the
- MakeProcInstance() is necessary, so each instance gets its own unique
- little header that Windows can patch.
-
- So, all that work is necessary, right? Wrong. Despite all the work
- that MakeProcInstance() and EXPORTS go through to put the correct
- value into the DS register, THAT VALUE WAS JUST SITTING IN ANOTHER
- REGISTER WAITING TO BE USED. Which register? SS.
-
- Remember that in a Windows application, SS == DS. Let me repeat
- that, SS == DS. Now, does any of the function prolog code or the
- instance thunk code do anything to SS? Nope. Whenever any of your
- application code is running, and whenever Windows calls one of your
- window functions or callback functions, SS contains your data segment
- address. The prolog code and instance thunks don't have anything to
- do with this; Windows' task manager puts the right value into SS
- before it lets your task run. If it didn't, the SS == DS assumption
- would be violated.
-
- You can probably guess by now what FIXDS does. It patches all FAR
- function prologs to look like this:
-
- mov ax, ss
- nop
- inc bp
- push bp
- mov bp, sp
- push ds
- mov ds, ax
-
- Now this prolog works for *all* FAR functions. Since SS, by
- definition, always has the correct data segment value, this prolog
- will put the correct value into DS. It doesn't matter whether it's a
- function you call directly or whether it is called back from Windows.
-
- This also explains why FIXDS allows you to call application functions
- directly from a DLL. Without FIXDS, you need the EXPORTS and
- MakeProcInstance() to get the proper value into DS, and the result of
- the MakeProcInstance() call isn't known till runtime, so the best you
- can do is an indirect call through the instance thunk. With FIXDS,
- however, the actual function entry point is perfectly usable
- regardless of where it is called from.
-
-
- The folks at Microsoft didn't believe me when I told them about the
- technique that FIXDS uses. After studying it a bit, they realized
- that of course it works. If it didn't, every application that's been
- compiled with the SS == DS assumption would have failed. Perhaps
- there's hope that a future version of Windows or the C compiler will
- have something like this built in. In the meantime, use FIXDS in
- your Windows applications and you'll never have to worry about
- EXPORTS and MakeProcInstance() again.
-
-
- Michael Geary
- P.O. Box 1479
- Los Gatos, CA 95031
-
- BIX: GEARY
- CompuServe: 76704,35
- GEnie: GEARY
-
- -----------------------------------------------------------------------------
-